package ap

import (
	"crypto/rsa"
	"fmt"
	"gitee.com/cic-cybertwin/utils/cryptotools"
	"gitee.com/cic-cybertwin/utils/protocol"

	"github.com/panjf2000/gnet/v2"
	"github.com/panjf2000/gnet/v2/pkg/logging"
	log "github.com/sirupsen/logrus"
)

type AP struct {
	gnet.BuiltinEventEngine
	eng        gnet.Engine
	Network    string
	Addr       string
	Multicore  bool
	privateKey *rsa.PrivateKey
	publicKey  *rsa.PublicKey
}

// connContext 对于每个连接，我们都要保存一个其对应的状态。对于其中的随机数，我们假定都是正数，当其值为零值时，就是还没初始化
type connContext struct {
	a   int    // 存储来自ue发送的随机数 a = g ^ r1 mod p
	r2  int    // 存储ap生成的随机数r2
	ck1 []byte // ck1 = a ^ r2 mod p
	ck2 []byte // ck2由ap产生，
}

func (ap *AP) LoadPrivateKey(dir string, filenameWithSuffix string) {
	key := cryptotools.RSA_LoadPrivateKey(dir, filenameWithSuffix)
	ap.privateKey = key
}

func (ap *AP) LoadPublicKey(dir string, filenameWithSuffix string) {
	key := cryptotools.RSA_LoadPublicKey(dir, filenameWithSuffix)
	ap.publicKey = key
}

func (ap *AP) OnBoot(eng gnet.Engine) (action gnet.Action) {
	logging.Infof("[OnBoot]-running RegisterCenter on %s with multi-core=%t",
		fmt.Sprintf("%s://%s", ap.Network, ap.Addr), ap.Multicore)
	ap.eng = eng
	return
}

func (ap *AP) OnOpen(conn gnet.Conn) (out []byte, action gnet.Action) {
	conn.SetContext(new(connContext))
	log.WithFields(log.Fields{
		"remote-peer-ip": conn.RemoteAddr().String(),
	}).Infoln("[OnOpen]-连接成功")
	return
}

func (ap *AP) OnTraffic(c gnet.Conn) (action gnet.Action) {
	codec := protocol.CodecUtils.AccessProtocolCodec
	var packets [][]byte
	for {
		data, typeName, _, err := codec.Decode(c)
		log.Infoln("[OnTraffic]-成功解析数据包")
		if err == protocol.ErrIncompleteBuf {
			break
		}
		if typeName == protocol.TYPE_ACCESSHELLO.GetName() {
			/*log.Infof("[OnTraffic]-[ACCESS HELLO]-数据包data=%s", string(data))
			// access hello类型，需要向客户端返回证书和公钥
			// 1. 加载来自ca/ap/publicKey.pem中的内容
			publicKeyFileName := "./ca/ap/publicKey.pem"
			certFileName := "./ca/ap/cert.crt"
			pkData, err := os.ReadFile(publicKeyFileName)
			if err != nil {
				log.Errorf("[OnTraffic]-[ACCESS HELLO]-读取%s文件失败", publicKeyFileName)
				return gnet.Close
			}
			certData, err := os.ReadFile(certFileName)
			accessHelloResp := &model.AccessHelloResp{
				PublicKey:   pkData,
				Certificate: certData,
			}
			respData, err := proto.Marshal(accessHelloResp)
			if err != nil {
				log.Errorf("[OnTraffic]-[ACCESS HELLO]-序列化accessHelloResp异常:" + err.Error())
				return gnet.Close
			}
			encode, _ := codec.Encode(respData, protocol.TYPE_ACCESSHELLO_RESP)
			log.Infoln("[OnTraffic]-[ACCESS HELLO]-封装TYPE_ACCESSHELLO_RESP的package")*/
			encode, err := ap.accessHelloHandler(c, data)
			if err != nil {
				log.Errorf("[OnTraffic]-[TYPE_ACCESSHELLO]-发送accesshello失败: err=%s", err.Error())
				return gnet.Close
			}
			packets = append(packets, encode)
		} else if typeName == protocol.TYPE_HID_CHECK.GetName() {
			/*request := model.HidCheckRequest{}
			err = proto.Unmarshal(data, &request)
			if err != nil {
				log.Errorf("[OnTraffic]-[ACCESS HELLO]-[hid check]-反序列化HidCheckRequest失败: %s", err.Error())
				return gnet.Close
			}
			// 校验数据的合法性
			// 根据hid查找数据库中该设备对应的公钥，以供verify使用
			query := session.NewDBQuery()
			hidAndR1, err := cryptotools.RSA_Decrypt(request.EncryptedData, ap.privateKey)
			splitRes := strings.Split(string(hidAndR1), stringutils.SEP)
			if err != nil {
				log.Errorln("[OnTraffic]-[Hid check阶段]-", err.Error())
			}
			ueInfoByHid, err := query.GetUeInfoByHid(context.Background(), splitRes[0])
			if err != nil {
				log.Errorln("[OnTraffic]-[Hid Check]-无法找到对应的hid:", err.Error())
				return gnet.Close
			}
			i, err := strconv.ParseInt(splitRes[1], 10, 64)
			if err != nil {
				log.Errorln("[OnTraffic]-[Hid Check]-解析r1异常:", err.Error())
				return gnet.None
			}
			cc.a = int(i)
			p, _ := pem.Decode([]byte(ueInfoByHid.UePk))
			if p == nil {
				log.Errorln("[OnTraffic]-[Hid Check]-Invalid Hid:", splitRes[0])
				return gnet.None
			}
			pk, err := x509.ParsePKCS1PublicKey(p.Bytes)
			if err != nil {
				log.Errorln("[OnTraffic]-[Hid Check]-设备公钥无法解析：", err.Error())
				return gnet.None
			}
			verify := cryptotools.RSA_SignVerify([32]byte(request.Hash), request.Signature, pk)
			if !verify {
				log.Errorln("[OnTraffic]-[Hid Check]-设备hash&signature无法校验失败")
				return gnet.None
			}
			// 拼接ctid+r2，用pkue加密，并用ap的私钥签名+哈希
			// 校验结束后，需要把ctid发送至ue
			r2 := generator.RandomInt(1, 32)
			cc.r2 = r2
			cc.ck1 = cryptotools.Int64ToBytes(cryptotools.Power(int64(cc.a), int64(cc.r2)) % cryptotools.MODULUS)
			cc.ck1 = append(cc.ck1, cc.ck1...)
			log.Infof("[OnTraffic]-[Hid Check]-获取到ck1:%v，原料中A=%d, r2 = %d", cryptotools.Power(int64(cc.a), int64(cc.r2))%cryptotools.MODULUS, cc.a, cc.r2)
			b := cryptotools.Power(cryptotools.BASE, int64(r2)) % cryptotools.MODULUS
			transferData := []byte(fmt.Sprintf("%s||%d", ueInfoByHid.CybertwinID, b))
			//log.Infof("[OnTraffic]-[Hid Check]-包装cybertwinId和r2随机数:%s", string(transferData))
			encryptTransferData, err := cryptotools.RSA_Encrypt(transferData, pk)
			if err != nil {
				log.Errorln("[OnTraffic]-[Hid Check]-使用pkue加密返回数据时出错:", err.Error())
				return gnet.Close
			}
			hash, signature := cryptotools.RSA_Sign(transferData, ap.privateKey)
			hidCheckResp := &model.HidCheckResp{
				EncryptedData: encryptTransferData,
				Hash:          hash[:],
				Signature:     signature,
			}
			hidCheckRespByte, err := proto.Marshal(hidCheckResp)
			if err != nil {
				log.Errorln("[OnTraffic]-[Hid Check]-序列化HidCheckResp异常:", err.Error())
				return gnet.Close
			}
			encode, _ := codec.Encode(hidCheckRespByte, protocol.TYPE_HID_CHECK_RESP)
			packets = append(packets, encode)
			cc.ck2 = []byte(generator.RandomString(32))
			// 作为客户端，建立和cybertwin的连接
			if !cybertwinExists(ueInfoByHid.CybertwinID) {
				createCybertwin(ueInfoByHid.CybertwinID)
			}

			// 建立与cybertwin的连接，向cybertwin发送ck2
			err = sendCk2ToCybertwin(ueInfoByHid, cc)
			if err != nil {
				log.Errorf("[OnTraffic]-[tls transfer ck2]-发送ck2密钥失败:%s", err.Error())
				return gnet.Close
			}*/
			err = ap.hidCheckHandler(c, data, packets)
			if err != nil {
				return gnet.Close
			}
		} else if typeName == protocol.TYPE_TOKEN_APPLY.GetName() {
			log.Infoln("[OnTraffic]-[Token Apply]-阶段开始")
			/*decryptData, err := cryptotools.AES_Decrypt(data, cc.ck1)
			if err != nil {
				log.Errorf("[OnTraffic]-[Token Apply]-使用ck1解密失败:%s", err.Error())
				return gnet.Close
			}
			tokenRequest := &model.TokenRequest{}
			err = proto.Unmarshal(decryptData, tokenRequest)
			if err != nil {
				log.Errorf("[OnTraffic]-[Token Apply]-反序列化tokenRequest失败:%s", err.Error())
				return gnet.Close
			}
			log.Infoln("[OnTraffic]-[Token Apply]-成功获取tokenRequest")
			// 包装token,token使用ck2进行加密,同时还要生成r3
			pasetoMaker, err := token.NewPasetoMaker(cc.ck2)
			if err != nil {
				log.Errorf("[OnTraffic]-[Token Apply]-无法生成PasetoMaker: %s", err.Error())
				return gnet.Close
			}
			r3 := generator.RandomInt(1, math.MaxInt32)
			pasetoToken, err := pasetoMaker.CreateToken(tokenRequest.Uid, tokenRequest.Ueid, time.Minute*5, int64(r3))
			if err != nil {
				log.Errorf("[OnTraffic]-[Token Apply]-生成token失败:%s", err.Error())
				return gnet.Close
			}
			if err != nil {
				log.Errorf("[OnTraffic]-[Token Apply]-使用ck2加密token失败:%s", err.Error())
				return gnet.Close
			}
			tokenResp := &model.TokenResp{Token: []byte(pasetoToken), R3: int32(r3)}
			tokenRespBytes, err := proto.Marshal(tokenResp)
			if err != nil {
				log.Errorf("[OnTraffic]-[Token Apply]-无法序列化TokenResp:%s", err.Error())
				return gnet.Close
			}
			encode, err := codec.AESEncode(tokenRespBytes, protocol.TYPE_TOKEN_APPLY_RESP, cc.ck1)
			if err != nil {
				log.Errorf("[OnTraffic]-[Token Apply]-无法使用ck1对tokenResp加密: %s", err.Error())
				return gnet.Close
			}
			packets = append(packets, encode)*/
			err = ap.tokenApplyHandler(c, data, packets)
			if err != nil {
				return gnet.Close
			}
		}
	}
	if n := len(packets); n > 1 {
		size, err := c.Writev(packets)
		if err != nil {
			log.Errorf("[OnTraffic]-[写回阶段]-write error:%s\n----------------------------", err.Error())
		}
		log.Infof("[OnTraffic]-[写回阶段]-Writev写入%d个数据，peer=%s\n----------------------------", size, c.RemoteAddr())
	} else if n == 1 {
		size, err := c.Write(packets[0])
		if err != nil {
			log.Errorf("[OnTraffic]-[写回阶段]-write error:%s\n----------------------------", err.Error())
		}
		log.Infof("[OnTraffic]-[写回阶段]-Write写入%d个数据, peer=%s\n----------------------------", size, c.RemoteAddr())
	}
	return gnet.None
}
